#coding=utf-8
#
# Copyright (C) 2014  NianNian TECH Co., Ltd. All rights reserved.
# Created on Apr 25, 2014, by Junn
# 
#
from core.models import BaseModel
from users.models import User
from django.db import models
import settings
from django.utils import timezone
from core.managers import BaseManager
from utils import eggs, logs
import urllib
import httplib
import json
from django.contrib.auth import get_user_model
from django.db.models.signals import post_save
from django.dispatch.dispatcher import receiver
from rest_framework.authtoken.models import Token


CODE_TYPES = (
    ('signup',  u'注册'),
    ('bind',    u'绑定手机'),
    ('reset',   u'重置密码')
)

# 云片短信服务设置       
YUNPIAN_SMS = {
    'apikey': 'ac1e4e1a95ef29c9aec13b7ae7177e53', 
    'host':   'yunpian.com',
    'port':   80,
    'sms_send_uri':  "/v1/sms/send.json",       #与sms_tpl_send_uri两者选其一
    
    'sms_tpl_send_uri': "/v1/sms/tpl_send.json"  #模板短信接口的URI 
}       

def send_sms(mobile, text):
    """ 通用接口发短信. 返回是否成功, 及对应的短信接口调用结果信息.
     
    @return: 发送成功返回True, 否则返回False
    """
    
    params = urllib.urlencode({'apikey': YUNPIAN_SMS.get('apikey'), 'text': text, 'mobile':mobile})
    headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
    conn = httplib.HTTPConnection(YUNPIAN_SMS.get('host'), port=YUNPIAN_SMS.get('port'), timeout=30)
    conn.request("POST", YUNPIAN_SMS.get('sms_send_uri'), params, headers)
    response = conn.getresponse()
    response_str = response.read()
    conn.close()
    
    result = json.loads(response_str)
    if result.get('code') == 0: #短信发送成功
        return True, result
    return False, result

def tpl_send_sms(mobile, tpl_id, tpl_value):
    """
    模板接口发短信
    """
    params = urllib.urlencode({'apikey': YUNPIAN_SMS.get('apikey'), 'tpl_id':tpl_id, 'tpl_value': tpl_value, 'mobile':mobile})
    headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
    conn = httplib.HTTPConnection(YUNPIAN_SMS.get('host'), port=YUNPIAN_SMS.get('port'), timeout=30)
    conn.request("POST", YUNPIAN_SMS.get('sms_tpl_send_uri'), params, headers)
    response = conn.getresponse()
    response_str = response.read()
    conn.close()
    
    result = json.loads(response_str)
    if result.get('code') == 0:     #短信发送成功
        return True, result
    return False, result    

class CodeRecordManager(BaseManager):
    
    def get_valid_code(self, phone, vcode, action):
        coderecord = self.filter(phone=phone, code=vcode, action=action, is_valid=True)
        if not coderecord:
            return None
        return coderecord[0]
    
    def check_code(self, phone, vcode, action='signup'):
        '''检查短信验证码, 所有验证码的有效期均为10分钟
        @return: 若验证码有效, 返回True, 否则返回False
        '''
        
        coderecord = self.get_valid_code(phone, vcode, action)
        if not coderecord:
            logs.info(u'Code record not found: phone %s, code %s, action %s' % (phone, vcode, action))
            return False
        
        logs.deb(__name__, eggs.lineno(), coderecord)
        
        if coderecord.is_expired():
            coderecord.is_valid=False   #验证成功后, 置为无效
            coderecord.save()
            logs.info(u'短信验证码已过期: (%s, %s)' % (phone, vcode))
            return False
            
        #coderecord.update(is_valid=False)   #验证成功后, 置为无效
        return True
    
    def gen_code(self, phone, action='signup', re_gen=False):
        """
        生成验证码.
        
        @param phone: 同一个手机号同一操作(注册/绑定/重置密码其中一种操作)一天之内最多能获取5次验证码. 
                     若该手机号码有未使用过的验证码(is_valid=True), 则不重新生成新的验证码
        @param action: 要生成的验证码类型                      
        @param re_gen: 当re_gen为True时, 将使该号码之前获取的所有验证码失效, 重新生成. 该参数设定可用控制短信发送数目
        """
        
        today = timezone.now().date()
        k = self.filter(phone=phone, action=action)
        if k.filter(is_valid=False, created_time__year=today.year, created_time__month=today.month,
                    created_time__day=today.day).count() >= 5:
            logs.warn(u'该手机号(%s, %s)一天之内已获取5次验证码' % (phone, action))
            return False, -1
        
        if re_gen: #若要重新生成验证码, 则将之前生成的验证码置为无效状态
            k.filter(is_valid=True).update(is_valid=False)
        
        #若存在未过期且有效可用的验证码, 则直接使用, 不重新生成
        k = k.filter(created_time__gt=timezone.now() - timezone.timedelta(minutes=10), is_valid=True)
        if k and k[0]: 
            code = k[0].code
        else:
            code = eggs.random_num()
            self.create(phone=phone, code=code, action=action)
        
        if not settings.SMS_CODE_REQUIRED:
            return True, code                   # Just for testing ...
        
        try:
            success, result = send_sms(phone, settings.SMS_TEXT.get(action).format(code))
            #success, result = tpl_send_sms(phone, settings.SMS_TPL.get(action), get_tpl_value(code))  # 模板方式发送短信
            if success:
                return success, code
            else:
                logs.error('send_sms error: \n %s' % result['msg'].encode('utf8'))
                return success, result
        except Exception, e:
            logs.error('Exception occurred when send_sms: \n %s' % e)
            return False, 0
    
def get_tpl_value(self, vcode):
    return '#code#=%s' % vcode
 
class CodeRecord(BaseModel):
    '''短信验证码记录'''
    phone = models.CharField(u'电话号码', max_length=16)
    code = models.CharField(u'验证码', max_length=16)
    action = models.CharField(u'操作类型', max_length=16, choices=CODE_TYPES)
    is_valid = models.BooleanField(u'是否可用', default=True)

    objects = CodeRecordManager()

    def __unicode__(self):
        return "%s, %s" % (self.phone, self.code)

    class Meta:
        verbose_name = u'短信验证码'
        verbose_name_plural = u'短信验证码'
        
    def check(self):
        pass    
    
    def is_expired(self):
        '''是否已过期'''
        delta = timezone.now() - self.created_time
        if delta.seconds > settings.SMS_CODE_EXPIRY * 60:
            return True
        return False
        
class AuthedApp(BaseModel):
    '''被授权的请求客户端'''
    app_key = models.CharField(u'客户端app_id', max_length=30, )
    secret_key = models.CharField(u'密钥', max_length=30)
    is_valid = models.BooleanField(u'是否可用', default=True)
    desc = models.CharField(u'描述', max_length=50, default='')

    class Meta:
        verbose_name = u'授权客户端'
        verbose_name_plural = u'授权客户端'
        
    def __unicode__(self):
        return self.app_key
    
    def check_secret_key(self, raw_key): #TODO
        return raw_key == self.secret_key
            
        #return check_password(raw_key, self.secret_key)    
        
CACHED_AUTHEDAPPS = {}        
def load_authedapps():
    clients = AuthedApp.objects.all()
    for c in clients:
        CACHED_AUTHEDAPPS[c.app_key] = c
    logs.info("Authed apps loaded.")    
        
def get_authedapp(app_key):
    if not CACHED_AUTHEDAPPS:
        load_authedapps() 
    return CACHED_AUTHEDAPPS.get(app_key, None)       
    
    
@receiver(post_save, sender=get_user_model())
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)
    
class OpenAccount(BaseModel):
    '''第3方登录账号'''
    
    user = models.ForeignKey(User)                                           # 关联user
    source_site = models.CharField(max_length=20)                            # 第三方站点名称
    openid = models.CharField(max_length=80)                                 # 第三方账号的uid
    access_token = models.CharField(max_length=120, blank=True, null=True)   # access_token
    expires_in = models.IntegerField(blank=True, null=True)                  # 过期时间
    
    def __unicode__(self):
        return u'%s, %s' % (self.user, self.source_site)

    class Meta:
        verbose_name = u'第3方登录账号'
        verbose_name_plural = u'第3方登录账号'
        
        